package cn.yl.pay.service.controller;

import cn.yl.api.server.result.Result;
import cn.yl.api.server.util.JwtUtil;
import cn.yl.pay.service.config.WechatPayConfig;
import cn.yl.pay.service.entity.PayOrderRecord;
import cn.yl.pay.service.entity.PayOrderVo;
import cn.yl.pay.service.service.RabbitmqService;
import cn.yl.pay.service.service.feign.CourseFeignService;
import cn.yl.pay.service.service.feign.UserCourseOrderFeignService;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author hyl
 * */

@RestController
@RequestMapping("pay")
@Slf4j
public class PayController {

    @Autowired
    private WechatPayConfig wechatPayConfig;
    @Autowired
    private CourseFeignService courseFeignService;

    @Autowired
    private UserCourseOrderFeignService userCourseOrderFeignService;

    @Autowired
    private RabbitmqService rabbitmqService;

    @Autowired
    private JwtUtil jwtUtil;

    @GetMapping("createCode")
    public Result createCode(Integer courseId, String coursename, String price, HttpServletRequest request) throws Exception {
        Map<String, String> resultMap = new HashMap<String, String>();
        String jwt = request.getHeader("jwt");
        Claims claims = jwtUtil.parseJWT(jwt);
        int userId = Integer.parseInt(claims.getId());
//        生成orderId  即订单id
        String orderId = WXPayUtil.generateNonceStr();
        String type = "CREATE";
        //通过userI,courseId,type 查找
        Map<String, String> myCourseOrderById = userCourseOrderFeignService.getMyCourseOrderById(userId, courseId, 1);
        String codeUrl = "";
        String status = "1";
        if (myCourseOrderById != null) {
            status = myCourseOrderById.get("status");
            if ("40".equals(status)) {
                status = "0";
                log.info(" code_url 已过期 ");

            }
            else if("20".equals(status)){
                return Result.error().setMessage("已购买过");
            }
            else {
                orderId = myCourseOrderById.get("orderId");
                codeUrl = myCourseOrderById.get("codeUrl");
                resultMap.put("code_url", codeUrl);
                resultMap.put("orderId", orderId);
                return Result.ok().setData("item", resultMap);
            }
        }

        PayOrderRecord payOrderRecord = new PayOrderRecord();
        payOrderRecord.setOrderNo(orderId).setCreatedBy(userId + "").setToStatus(status).setType(type);
        rabbitmqService.sendPayOrderRecord(payOrderRecord);
        log.info("发送至消息队列");
//        this.userCourseOrderFeignService.savePayOrderRecord(payOrderRecord);
        Map<String, Object> course = courseFeignService.getCourseById(courseId);
        WXPay wxpay = new WXPay(wechatPayConfig);
        Map<String, String> data = new HashMap<String, String>();
        data.put("body", coursename);
        data.put("out_trade_no", orderId);
        data.put("device_info", "WEB");
        // 货币类型
        data.put("fee_type", "CNY");
        // 订单金额,单位分
        log.info("total_fee", course.get(price));
        data.put("total_fee", "1");
        data.put("spbill_create_ip", request.getRemoteAddr());
        data.put("notify_url", "https://0ea3-27-19-195-203.jp.ngrok.io/pay/pay/wxpay/notify");
        // 此处指定为扫码支付
        data.put("trade_type", "NATIVE");
        data.put("product_id", courseId + "");
        try {
            Map<String, String> resp = wxpay.unifiedOrder(data);
            log.info("resp", resp);
            codeUrl = resp.get("code_url");
            resultMap.put("code_url", codeUrl);
            resultMap.put("orderId", orderId);
        } catch (Exception e) {
            e.printStackTrace();
        }

        PayOrderVo payOrderVo = new PayOrderVo();
        payOrderVo.setOrderNo(orderId)
                .setCourseId(courseId)
                .setSource(3)
                .setProductId(courseId)
                .setProductName(coursename)
                .setAmount(new BigDecimal(course.get("price").toString()))
                .setCodeUrl(codeUrl)
                .setUserId(userId);
        // 异步存入订单
        userCourseOrderFeignService.saveOrder(payOrderVo);
        //5.将map返回给浏览器
        return Result.ok().setData("item", resultMap);
    }

    private final ReentrantLock lock = new ReentrantLock();

    @PostMapping("wxpay/notify")
    public String WxNotify(HttpServletRequest request) {
        String notifyData = requestToStr(request);
        WXPay wxPay = new WXPay(wechatPayConfig);
        String resultStr = null;
        String status = "-1";
        String type = "PAY";
        log.info("  微信回调   ");
        if(lock.tryLock()){
            log.info("lock上锁");
            try {
                Map<String, String> xmlToMap = WXPayUtil.xmlToMap(notifyData);
                Map<String, String> payOrder = this.userCourseOrderFeignService.getPayOrder(xmlToMap.get("out_trade_no"));
                Map<String, String> payOrderMap = new HashMap<>();
                HashMap<String, String> resultMap = new HashMap<>();
                // 判断是否是成功的
                if ("2".equals(payOrder.get("status")))  {
                    log.info("出现重复消费");
                    resultMap.put("return_code", "SUCCESS");
                    resultStr = WXPayUtil.mapToXml(resultMap);
                    return resultStr;
                }
                if (wxPay.isPayResultNotifySignatureValid(xmlToMap)) {
                    log.info("验签成功");
//                    try {
//                        log.info("睡眠开始，模拟并发");
//                        TimeUnit.SECONDS.sleep(5);
//                        log.info("睡眠结束");
//                    }catch (Exception e){
//                        log.info(String.valueOf(e));
//                    }
                    payOrderMap = this.userCourseOrderFeignService.updateByStatus(xmlToMap.get("out_trade_no"), 2);
                    //todo 验证金额、添加日志 特殊情况处理
                    BigDecimal cashFee = new BigDecimal(xmlToMap.get("cash_fee"));
                    BigDecimal payFee = this.userCourseOrderFeignService.getCurrentPrice(xmlToMap.get("out_trade_no"));
                    if (cashFee.compareTo(payFee) == 0) {
                        log.info("价格相等");
                        status = "20";
                    } else {
                        //todo 修改为 status = 3
                        payOrderMap = this.userCourseOrderFeignService.updateByStatus(xmlToMap.get("out_trade_no"), 2);
                        status = "20";
                        //todo  价格 对照 回调处
//                        status = "price error";
                        log.info("价格不等");
                    }
//                if(cashFee.compareTo(payFee)==0){
                    int courseId = Integer.parseInt(payOrderMap.get("courseId"));
                    this.courseFeignService.updateSalesByCourseId(courseId);
                    resultMap.put("return_code", "SUCCESS");
                    resultStr = WXPayUtil.mapToXml(resultMap);
//                }else {
//                    // 异常支付金额处理
//                }
                } else {
                    status = "error to pay";
                }
                PayOrderRecord payOrderRecord = new PayOrderRecord();
                payOrderRecord.setOrderNo(payOrderMap.get("orderId")).setCreatedBy(payOrderMap.get("userId")).setToStatus(status).setType(type);
                        log.info("发送至消息队列");
                rabbitmqService.sendPayOrderRecord(payOrderRecord);
//                this.userCourseOrderFeignService.savePayOrderRecord(payOrderRecord);
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                log.info("unlock 解锁");
                lock.unlock();
            }
        }

        log.info("成功结束微信回调"+resultStr);
        return resultStr;
    }

    @GetMapping("checkOrderStatus/{orderId}")
    public Result checkOrderStatus(@PathVariable String orderId) throws Exception {
        WXPay wxpay = new WXPay(wechatPayConfig);
        Map<String, String> data = new HashMap<String, String>();
        data.put("out_trade_no", orderId);
        //第一次询问时间
        long beginTime = System.currentTimeMillis();
        this.userCourseOrderFeignService.updateByStatus(orderId, 1);
//        rabbitmqService.sendWork();
//        log.info("发送至消息队列");
        //不停的去微信后台询问是否支付
        while (true) {
            Map<String, String> resultMap = wxpay.orderQuery(data);
            //5.将map转成json并返回给浏览器
            //已经成功支付，停止询问
            if (resultMap.get("trade_state").equalsIgnoreCase("success")) {
                return Result.ok().setData("item", resultMap);
            }
            //超过30秒未支付，停止询问
            if (System.currentTimeMillis() - beginTime > 30000) {
                return Result.ok().setData("item", resultMap);
            }
            //每隔3秒，询问一次微信后台
            Thread.sleep(3000);
        }
    }

    private String requestToStr(HttpServletRequest request) {
        InputStream inputStream = null;
        ByteArrayOutputStream outputStream = null;
        String resultXml = null;
        try {
            inputStream = request.getInputStream();
            outputStream = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, len);
            }
            resultXml = new String(outputStream.toByteArray(), "utf-8");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != outputStream) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != inputStream) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return resultXml;
    }

}
